<template>
	<view class="uqrcode" :style="{ width: `${size}px`, height: `${size}px` }">
		<!-- canvas模式，默认 -->
		<block v-if="mode === 'canvas'"><canvas class="uqrcode-canvas" :id="id" :canvas-id="id" :style="{ width: `${size}px`, height: `${size}px` }"></canvas></block>

		<!-- view模式，兼容 -->
		<view
			v-else-if="mode === 'view'"
			class="uqrcode-view"
			:style="{
				width: `${size}px`,
				height: `${size}px`,
				padding: `${margin}px`,
				'background-color': backgroundColor
			}"
		>
			<view class="uqrcode-view-row" v-for="(row, rowIndex) in modules.length" :key="rowIndex">
				<view
					class="uqrcode-view-col"
					v-for="(col, colIndex) in modules.length"
					:key="colIndex"
					:style="{
						width: `${tileSize}px`,
						height: `${tileSize}px`,
						'background-color': modules[rowIndex][colIndex] ? foregroundColor : backgroundColor
					}"
				></view>
			</view>
		</view>

		<!-- H5保存提示 -->
		<!-- #ifdef H5 -->
		<view class="uqrcode-h5-save" v-if="isH5Save">
			<image class="uqrcode-h5-save-image" :src="tempFilePath"></image>
			<text class="uqrcode-h5-save-text">请长按二维码进行保存</text>
			<view class="uqrcode-h5-save-close" @click="isH5Save = false">
				<view class="uqrcode-h5-save-close-before"></view>
				<view class="uqrcode-h5-save-close-after"></view>
			</view>
		</view>
		<!-- #endif -->
	</view>
</template>

<script>
import uqrcode from './uqrcode.js';

export default {
	name: 'uqrcode',
	props: {
		// id
		id: {
			type: String,
			default: uuid()
		},
		// 生成模式
		mode: {
			type: String,
			default: 'canvas' // canvas|view (nvue不支持canvas模式)
		},
		// 二维码内容
		text: String,
		// 二维码大小
		size: {
			type: Number,
			default: 190
		},
		// 填充边距
		margin: {
			type: Number,
			default: 0
		},
		// 背景色
		backgroundColor: {
			type: String,
			default: '#fff'
		},
		// 前景色
		foregroundColor: {
			type: String,
			default: '#000'
		},
		// 纠错等级
		errorCorrectLevel: {
			type: [String, Number],
			default: uqrcode.errorCorrectLevel.H
		},
		// 版本
		typeNumber: {
			type: Number,
			default: -1
		},
		// 导出的文件类型
		fileType: {
			type: String,
			default: 'png'
		}
	},
	data() {
		return {
			canvasContext: null,
			makeing: false,
			delegate: null,
			delegateParams: null,
			tempFilePath: '',
			isH5Save: false,
			isReload: false
		};
	},
	computed: {
		modules() {
			let options = {
				...this.$props
			};
			if (typeof options.errorCorrectLevel === 'string') {
				options.errorCorrectLevel = uqrcode.errorCorrectLevel[options.errorCorrectLevel];
			}
			return uqrcode.getModules(options);
		},
		tileSize() {
			return (this.size - this.margin * 2) / this.modules.length;
		}
	},
	watch: {
		/* 深度监听props，任意属性一发生改变立即重绘二维码 */
		$props: {
			handler() {
				this.reload();
			},
			deep: true
		},
		makeing(val) {
			if (!val) {
				if (typeof this.delegate === 'function') {
					this.delegate(this.delegateParams);
				}
			}
		}
	},
	mounted() {
		this.$nextTick(() => {
			this.make();
		});
	},
	methods: {
		reload() {
			/* 重载组件 */
			this.isReloadMake = true;
			this.isReload = true;
			this.$nextTick(() => {
				this.isReload = false;
				this.$nextTick(() => {
					setTimeout(() => {
						this.make();
					}, 150);
				});
			});
		},
		make() {
			if (this.makeing) {
				return;
			}
			this.makeing = true;
			if (this.mode === 'canvas') {
				let ctx = null;

				/* 获取绘图所需的上下文 */
				ctx = uni.createCanvasContext(this.id, this);

				this.canvasContext = ctx;

				ctx.draw(); // 清空之前的画布内容
				ctx.setFillStyle(this.backgroundColor);
				ctx.fillRect(0, 0, this.size, this.size);

				for (var row = 0; row < this.modules.length; row++) {
					for (var col = 0; col < this.modules.length; col++) {
						// 计算每一个小块的位置
						var x = col * this.tileSize + this.margin;
						var y = row * this.tileSize + this.margin;
						var w = this.tileSize;
						var h = this.tileSize;

						var style = this.modules[row][col] ? this.foregroundColor : this.backgroundColor;
						ctx.setFillStyle(style);
						ctx.fillRect(x, y, w, h);
					}
				}

				ctx.draw(false, () => {
					// setTimeout(() => {
					this.complete();
					// }, 3000)
				});
			} else if (this.mode === 'view') {
				this.complete();
			}
		},
		complete(e = {}) {
			let basic = {
				id: this.id,
				text: this.text,
				mode: this.mode
			};
			let ages = {
				...basic,
				...e
			};
			this.makeing = false;
			this.$emit('complete', ages);
		},
		toTempFilePath(callback = {}) {
			if (typeof callback.success != 'function') {
				callback.success = () => {};
			}
			if (typeof callback.fail != 'function') {
				callback.fail = () => {};
			}
			if (typeof callback.complete != 'function') {
				callback.complete = () => {};
			}

			if (this.makeing) {
				// 如果还在生成状态，那当前操作将托管到委托，监听生成完成后再通过委托复调当前方法
				this.delegate = this.toTempFilePath;
				this.delegateParams = callback;
				return;
			} else {
				this.delegate = null;
				this.delegateParams = null;
			}

			uni.canvasToTempFilePath(
				{
					canvasId: this.id,
					fileType: this.fileType,
					width: this.size,
					height: this.size,
					success: (res) => {
						this.tempFilePath = res.tempFilePath;
						callback.success(res);
					},
					fail: (err) => {
						callback.fail(err);
					},
					complete: (res) => {
						callback.complete(res);
					}
				},
				this
			);
		},
		save(callback = {}) {
			if (typeof callback.success != 'function') {
				callback.success = () => {};
			}
			if (typeof callback.fail != 'function') {
				callback.fail = () => {};
			}
			if (typeof callback.complete != 'function') {
				callback.complete = () => {};
			}

			this.toTempFilePath({
				success: (res) => {
					// #ifdef H5
					this.isH5Save = true;
					callback.success({
						msg: 'H5请长按图片保存'
					});
					callback.complete({
						msg: 'H5请长按图片保存'
					});
					// #endif

					// #ifndef H5
					uni.saveImageToPhotosAlbum({
						filePath: this.tempFilePath,
						success: (res1) => {
							callback.success({
								msg: '保存成功'
							});
						},
						fail: (err1) => {
							callback.fail(err1);
						},
						complete: (res1) => {
							callback.complete(res1);
						}
					});
					// #endif
				},
				fail: (err) => {
					callback.fail(err);
				}
			});
		}
	}
};

function uuid(len = 32, firstU = true, radix = null) {
	let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
	let uuid = [];
	radix = radix || chars.length;

	if (len) {
		// 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
		for (let i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)];
	} else {
		let r;
		// rfc4122标准要求返回的uuid中,某些位为固定的字符
		uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
		uuid[14] = '4';

		for (let i = 0; i < 36; i++) {
			if (!uuid[i]) {
				r = 0 | (Math.random() * 16);
				uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r];
			}
		}
	}

	// 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class
	if (firstU) {
		uuid.shift();
		return 'u' + uuid.join('');
	} else {
		return uuid.join('');
	}
}
</script>

<style>
.uqrcode {
	position: relative;
}

.uqrcode-makeing {
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	display: flex;
	box-sizing: border-box;
	justify-content: center;
	align-items: center;
	background-color: #fff;
	transition-property: opacity;
	transition-duration: 0.25s;
	opacity: 0.88;
}

.uqrcode-make-complete {
	opacity: 0;
}

.uqrcode-view {
	display: flex;
	box-sizing: border-box;
	flex-direction: column;
}

.uqrcode-view-row {
	display: flex;
	box-sizing: border-box;
	flex-direction: row;
}

.uqrcode-view-col {
	display: flex;
	box-sizing: border-box;
}

/* #ifdef H5 */
.uqrcode-h5-save {
	position: fixed;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: 100;
	background-color: rgba(0, 0, 0, 0.68);
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
}

.uqrcode-h5-save-image {
	width: 512rpx;
	height: 512rpx;
	padding: 32rpx;
}

.uqrcode-h5-save-text {
	margin-top: 20rpx;
	font-size: 32rpx;
	font-weight: 700;
	color: #fff;
}

.uqrcode-h5-save-close {
	position: relative;
	margin-top: 72rpx;
	width: 40rpx;
	height: 40rpx;
	border: 2rpx solid #fff;
	border-radius: 40rpx;
	padding: 10rpx;
}

.uqrcode-h5-save-close-before {
	position: absolute;
	top: 50%;
	transform: translateY(-50%) rotate(45deg);
	width: 40rpx;
	height: 4rpx;
	background: #fff;
}

.uqrcode-h5-save-close-after {
	position: absolute;
	top: 50%;
	transform: translateY(-50%) rotate(-45deg);
	width: 40rpx;
	height: 4rpx;
	background: #fff;
}

/* #endif */
</style>
